/*
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

/*
 *
 *
 *
 *
 *
 * Written by Doug Lea with assistance from members of JCP JSR-166
 * Expert Group and released to the public domain, as explained at
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

package java.util.concurrent.locks;

import sun.misc.Unsafe;

/**
 * LockSupport用来创建锁和其他同步类的基本线程阻塞原语。
 * 1、当调用LockSupport.park  时，表示当前线程将会等待，直至获得许可
 * 2、当调用LockSupport.unpark时，必须把等待获得许可的线程作为参数进行传递，好让此线程继续运行

 * 当线程调用LockSupport.park()方法时，如果线程已经持有许可证，则会立即返回
 * 否则，该线程会被阻塞挂起，此时线程不会占用CPU资源。挂起的线程会失去CPU的控制权，
 * 直到某种条件成立(如果其他线程调用了unpark()方法、线程中断或者发生不可预料的事情)才能继续运行

 * 3种让线程等待和唤醒的方法
 *  1、jvm: Object-wait()方法让线程等待，Object-notify()方法唤醒线程
 *  2、jdk: JUC-Condition-await()方法让线程等待，signal()方法唤醒线程
 *  3、LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程
 */
public class LockSupport {


    /**
     * Thread.sleep()、Object.wait()、Condition.await()、LockSupport.park()的区别? 重点

     * 一、Thread.sleep()和Object.wait()的区别
     *    1、Thread.sleep()不会释放占有的锁，Object.wait()会释放占有的锁；
     *    2、Thread.sleep()必须传入时间，Object.wait()可传可不传，不传表示一直阻塞下去
     *    3、Thread.sleep()到时间了会自动唤醒，然后继续执行
     *    4、Object.wait()不带时间的，需要另一个线程使用Object.notify()唤醒
     *    5、Object.wait()带时间的，假如没有被notify，到时间了会自动唤醒，
     *      这时又分好两种情况，一是立即获取到了锁，线程自然会继续执行；二是没有立即获取锁，线程进入同步队列等待获取锁；
     *    其实，他们俩最大的区别就是Thread.sleep()不会释放锁资源，Object.wait()会释放锁资源

     * 二、Object.wait()和Condition.await()的区别
     *    1、Object.wait()和Condition.await()的原理是基本一致的，不同的是Condition.await()底层是调用LockSupport.park()来实现阻塞当前线程的。
     *    2、实际上，它在阻塞当前线程之前还干了两件事，
     *       一是把当前线程添加到条件队列中
     *       二是“完全”释放锁，也就是让state状态变量变为0，然后才是调用LockSupport.park()阻塞当前线程。

     * 三、Thread.sleep()和LockSupport.park()的区别
     *    1、Thread.sleep()和LockSupport.park()方法类似，都是阻塞当前线程的执行，且都不会释放当前线程占有的锁资源；
     *    2、Thread.sleep()没法从外部唤醒，只能自己醒过来
     *    3、LockSupport.park()方法可以被另一个线程调用LockSupport.unpark()方法唤醒
     *    4、Thread.sleep()方法声明上抛出了InterruptedException中断异常，所以调用者需要捕获这个异常或者再抛出
     *    5、LockSupport.park()方法不需要捕获中断异常；
     *    6、Thread.sleep()本身就是一个native方法
     *    7、LockSupport.park()底层是调用的Unsafe的native方法；

     * 四、Object.wait()和LockSupport.park()的区别
     *    1、Object.wait()方法需要在synchronized块中执行
     *    2、LockSupport.park()可以在任意地方执行
     *    3、Object.wait()方法声明抛出了中断异常，调用者需要捕获或者再抛出；
     *    4、LockSupport.park()不需要捕获中断异常；
     *    5、Object.wait()不带超时的，需要另一个线程执行notify()来唤醒，但不一定继续执行后续内容
     *    6、LockSupport.park()不带超时的，需要另一个线程执行unpark()来唤醒，一定会继续执行后续内容；
     *    park()/unpark()底层的原理是“二元信号量”，你可以把它相像成只有一个许可证的Semaphore，只不过这个信号量在重复执行unpark()的时候也不会再增加许可证，最多只有一个许可证。

     * 五、锁资源操作:
     *    1、Thread.sleep(): 不涉及锁资源。
     *    2、Object.wait(): 必须在同步块或同步方法中使用，并释放锁。
     *    3、Condition.await(): 必须与Lock对象一起使用，并释放锁。
     *    4、LockSupport.park(): 不涉及锁资源。

     * 六、CPU资源操作:
     *    1、Thread.sleep(): 使当前线程进入休眠状态，释放CPU资源。
     *    2、Object.wait(): 使当前线程进入等待状态，释放CPU资源。
     *    3、Condition.await(): 使当前线程进入等待状态，释放CPU资源。
     *    4、LockSupport.park(): 使当前线程进入等待状态，释放CPU资源。
     */






    // 通过内部API实现热点
    // UNSAFE字段表示 sun.misc.Unsafe类
    // 一般程序中不允许直接调用
    private static final sun.misc.Unsafe UNSAFE;

    // 表示内存偏移地址
    // 而long型的表示Thread实例对象相应字段在内存中的偏移地址，可以通过该偏移地址获取或者设置该字段的值。
    private static final long parkBlockerOffset;
    // 表示Thread类中的threadLocalRandomSeed对象的内存偏移地址
    private static final long SEED;
    // 表示Thread类中的threadLocalRandomProbe对象的内存偏移地址
    private static final long PROBE;
    // 表示Thread类中的threadLocalRandomSecondarySeed对象的内存偏移地址
    private static final long SECONDARY;

    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> tk = Thread.class;
            parkBlockerOffset = UNSAFE.objectFieldOffset
                    (tk.getDeclaredField("parkBlocker"));
            SEED = UNSAFE.objectFieldOffset
                    (tk.getDeclaredField("threadLocalRandomSeed"));
            PROBE = UNSAFE.objectFieldOffset
                    (tk.getDeclaredField("threadLocalRandomProbe"));
            SECONDARY = UNSAFE.objectFieldOffset
                    (tk.getDeclaredField("threadLocalRandomSecondarySeed"));
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }




    // 私有构造函数，无法被实例化
    private LockSupport() {
    }

    private static void setBlocker(Thread t, Object arg) {
        UNSAFE.putObject(t, parkBlockerOffset, arg);
    }

    /**
     * 恢复当前线程
     */
    public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }

    /**
     * 暂停当前线程
     */
    public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, 0L);
        setBlocker(t, null);
    }

    /**
     * 暂停当前线程，不过有超时时间的限制
     */
    public static void parkNanos(Object blocker, long nanos) {
        if (nanos > 0) {
            Thread t = Thread.currentThread();
            setBlocker(t, blocker);
            UNSAFE.park(false, nanos);
            setBlocker(t, null);
        }
    }

    /**
     * 暂停当前线程，直到某个时间
     */
    public static void parkUntil(Object blocker, long deadline) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(true, deadline);
        setBlocker(t, null);
    }

    /**
     * 获取线程的Blocker对象
     */
    public static Object getBlocker(Thread t) {
        if (t == null)
            throw new NullPointerException();
        return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
    }


    /**
     * 无期限暂停当前线程
     */
    public static void park() {
        UNSAFE.park(false, 0L);
    }


    /**
     * 暂停当前线程，不过有超时时间的限制
     */
    public static void parkNanos(long nanos) {
        if (nanos > 0)
            UNSAFE.park(false, nanos);
    }


    /**
     * 暂停当前线程，直到某个时间
     */
    public static void parkUntil(long deadline) {
        UNSAFE.park(true, deadline);
    }


    static final int nextSecondarySeed() {
        int r;
        Thread t = Thread.currentThread();
        if ((r = UNSAFE.getInt(t, SECONDARY)) != 0) {
            r ^= r << 13;   // xorshift
            r ^= r >>> 17;
            r ^= r << 5;
        } else if ((r = java.util.concurrent.ThreadLocalRandom.current().nextInt()) == 0)
            r = 1; // avoid zero
        UNSAFE.putInt(t, SECONDARY, r);
        return r;
    }



}
